/* Copyright (C) 2005, 2006 by Daniel Gheorghian
 All rights reserved
 
 Use and distribute freely, except: don't remove my name from the source or documentation 
 (don't take credit for my work), mark your changes (don't get me blamed for your possible bugs),
 don't alter or remove this notice.
 
 No warrantee of any kind, express or implied, is included with this software; use at your
 own risk, responsibility for damages (if any) to anyone resulting from the use of this
 software rests entirely with the user.
 
 Send bug reports, bug fixes, enhancements, requests, flames, etc., and I'll try to keep 
 a version up to date.  I can be reached at: daniel.gheorghian@gmail.com
*/


package com.ds.components.grid;

import com.ds.components.interactors.Interactor;
import com.ds.components.interactors.InteractorEvent;
import com.ds.components.layout.DisplayLayoutInterface;
import com.ds.renderer.CellRenderer;
import com.ds.theme.ColorTheme;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

public class DisplayGridUI extends AbstractDisplayGridUI
{
	private DisplayGrid d;
	private Map<String, Boolean> memory = new Hashtable<String, Boolean>();

	public static ComponentUI createUI( JComponent c )
	{
		return new DisplayGridUI();
	}

	public void installUI( JComponent c )
	{
		this.d = ( DisplayGrid ) c;
		c.addMouseListener( this );
		c.addMouseMotionListener( this );
	}

	public void uninstallUI( JComponent c )
	{
		c.removeMouseListener( this );
		c.removeMouseMotionListener( this );
	}

	public void paint( Graphics g, JComponent c )
	{
		Graphics2D g2D = ( Graphics2D ) g;
		g2D.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );

		g2D.setColor( c.getBackground() );
		g2D.fillRect( 0, 0, c.getWidth(), c.getHeight() );

		paintCells( g2D, c );

		if( d.isDrawGrid() )
		{
			paintGridLines( g2D, c );
		}

		if( d.getGlassPaneRenderer() != null )
		{
			DisplayLayoutInterface layout = d.getDisplayLayout();
			Rectangle r = new Rectangle( layout.getLeftHeaderWidth(), layout.getTopHeaderHeight(),
			                             layout.getColumnsNo() * layout.getCellWidth(),
			                             layout.getRowsNo() * layout.getCellHeight() );
			Graphics newG = g2D.create( r.x, r.y, r.width, r.height );
			d.getGlassPaneRenderer().render( newG, d, false, false, false );
		}

		if( d.getTopHeaderRenderer() != null )
		{
			paintGridTopHeader( g2D, c );
		}

		if( d.getLeftHeaderRenderer() != null )
		{
			paintGridRightHeader( g2D, c );
		}
	}

	protected void paintCells( Graphics2D g2D, JComponent c )
	{
		DisplayGrid d = ( DisplayGrid ) c;
		DisplayLayoutInterface layout = d.getDisplayLayout();
		CellRenderer renderer;
		Object obj;
		Rectangle r;
		Iterator<GridCell> it = layout.iterator();
		GridCell cell;

		while( it.hasNext() )
		{
			cell = it.next();
			r = cell.getBounds();
			obj = d.getItem( new Position( cell.getRow(), cell.getColumn() ) );

			if( obj != null )
			{
				renderer = d.getRenderer( obj.getClass() );
				paintCell( g2D, r, obj, renderer, cell.getColorTheme(), cell.isDisabled(), cell.isFlash() );
			}
		}
	}

	protected void paintCell( Graphics2D g2D, Rectangle r, Object obj, CellRenderer renderer, ColorTheme colorTheme, Boolean disabled, Boolean flash )
	{
		Graphics g = g2D.create( r.x, r.y, r.width, r.height );
		renderer.renderCell( g, obj, false, colorTheme, disabled, flash );
	}

	protected void paintCell( GridCell cell )
	{
		if( cell == null )
		{
			return;
		}

		Object obj = d.getItem( new Position( cell.getRow(), cell.getColumn() ) );
		Rectangle r = cell.getBounds();
		Graphics2D g2D = ( Graphics2D ) d.getGraphics();

		if( g2D != null && obj != null && r != null )
		{
			CellRenderer renderer = d.getRenderer( obj.getClass() );

			if( cell.isFlash() )
			{
				paintCell( g2D, r, obj, renderer, cell.getColorTheme(), cell.isDisabled(), true );
				try
				{
					Thread.sleep( 30 );
				}
				catch( InterruptedException ex )
				{
					ex.printStackTrace();
				}
				d.repaint();
				return;
			}

			paintCell( g2D, r, obj, renderer, cell.getColorTheme(), cell.isDisabled(), false );
		}
	}

	private void paintGridLines( Graphics2D g2D, JComponent c )
	{
		DisplayGrid d = ( DisplayGrid ) c;
		DisplayLayoutInterface layout = d.getDisplayLayout();
		int width = layout.getCellWidth() + 2 * layout.getHGap();
		int height = layout.getCellHeight() + 2 * layout.getVGap();
		int hOffset = layout.getLeftHeaderWidth();
		int vOffset = layout.getTopHeaderHeight();

		g2D.setColor( d.getGridColor() );
		g2D.setStroke( new BasicStroke( 1F ) );

		for( int column = 0; column < layout.getColumnsNo(); column++ )
		{
			for( int row = 0; row < layout.getRowsNo(); row++ )
			{
				g2D.drawRect( hOffset + column * width, vOffset + row * height, width, height );
			}
		}
	}

	private void paintGridTopHeader( Graphics2D g2D, JComponent c )
	{
		DisplayGrid d = ( DisplayGrid ) c;
		DisplayLayoutInterface layout = d.getDisplayLayout();
		d.topHeaderRenderer.setHOffset( layout.getTopHeaderHeight() );
		d.topHeaderRenderer.setVOffset( layout.getLeftHeaderWidth() );
		d.topHeaderRenderer.renderHeader( g2D, layout.getCellWidth(),
		                                  layout.getCellHeight(),
		                                  layout.getColumnsNo(),
		                                  layout.getRowsNo() );
	}

	private void paintGridRightHeader( Graphics2D g2D, JComponent c )
	{
		DisplayGrid d = ( DisplayGrid ) c;
		DisplayLayoutInterface layout = d.getDisplayLayout();
		d.leftHeaderRenderer.setHOffset( layout.getTopHeaderHeight() );
		d.leftHeaderRenderer.setVOffset( layout.getLeftHeaderWidth() );
		d.leftHeaderRenderer.renderHeader( g2D, layout.getCellWidth(),
		                                   layout.getCellHeight(),
		                                   layout.getColumnsNo(),
		                                   layout.getRowsNo() );
	}

	public void mouseClicked( MouseEvent e )
	{
		if( d.interactors.get( Interactor.ON_MOUSE_CLICKED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
			d.fireInteractors( Interactor.ON_MOUSE_CLICKED, ev );

			GridCell cell = d.layout.getGridCell( pos );

			if( cell != null )
			{
				cell.setFlash( true );
				paintCell( cell );
			}
		}
	}

	public void mousePressed( MouseEvent e )
	{
		this.memory.clear();

		if( d.interactors.get( Interactor.ON_MOUSE_PRESSED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
			d.fireInteractors( Interactor.ON_MOUSE_PRESSED, ev );
			d.repaint();
			paintCell( d.layout.getGridCell( pos ) );
		}
	}

	public void mouseReleased( MouseEvent e )
	{
		this.memory.clear();

		if( d.interactors.get( Interactor.ON_MOUSE_RELEASED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
			d.fireInteractors( Interactor.ON_MOUSE_RELEASED, ev );
			d.repaint();
			paintCell( d.layout.getGridCell( pos ) );
		}
	}

	public void mouseEntered( MouseEvent e )
	{
		if( d.interactors.get( Interactor.ON_MOUSE_ENTERED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
			d.fireInteractors( Interactor.ON_MOUSE_ENTERED, ev );
			d.repaint();
			paintCell( d.layout.getGridCell( pos ) );
		}
	}

	public void mouseExited( MouseEvent e )
	{
		if( d.interactors.get( Interactor.ON_MOUSE_EXITED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
			d.fireInteractors( Interactor.ON_MOUSE_EXITED, ev );
			d.repaint();
			paintCell( d.layout.getGridCell( pos ) );
		}

	}

	public void mouseDragged( MouseEvent e )
	{
		if( d.interactors.get( Interactor.ON_MOUSE_DRAGGED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			if( this.memory.get( pos.toString() ) == null )
			{
				InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
				d.fireInteractors( Interactor.ON_MOUSE_DRAGGED, ev );
				d.repaint();
				paintCell( d.layout.getGridCell( pos ) );
				this.memory.put( pos.toString(), true );
			}

		}

	}

	public void mouseMoved( MouseEvent e )
	{
		if( d.interactors.get( Interactor.ON_MOUSE_MOVED ) != null )
		{
			Position pos = d.layout.getCellPosition( e.getPoint() );

			if( pos == null )
			{
				return;
			}

			InteractorEvent ev = new InteractorEvent( d, d.getItem( pos ), pos.getRow(), pos.getColumn() );
			d.fireInteractors( Interactor.ON_MOUSE_MOVED, ev );
			d.repaint();
			paintCell( d.layout.getGridCell( pos ) );
		}

	}
}
